#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/timerfd.h>
#include <time.h>
#include <unistd.h>
#include <poll.h>   // For poll()
#include <stdint.h> // For uint64_t
#include "MQTTClient.h"
#include "cJSON.h"
#include "shmem.h"

#define ADDRESS "tcp://mqtt.yyzlab.com.cn:1883"
#define CLIENTID "ExampleClientSub"
#define TOPIC "2423225385/Device2AIOTSIM"
#define QOS 1
#define TIMEOUT 10000L

volatile MQTTClient_deliveryToken deliveredtoken;
char config[4024] = "";

union val_t
{
    int b_val;   // bool类型存储空间
    int i_val;   // 整形值存储空间
    float f_val; // 浮点值存储空间
};

struct std_node
{
    int key;             // 唯一键值
    int type;            // 数据点类型
    int dev_type;        // 数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; // 变化上报后需要更新旧值
    union val_t new_val; // 从共享内存取出最新数据，放到new_val中
    int ret;             // 默认为-1，采集成功后设置为0，采集失败再置-1
};

char *serialize_info(struct std_node *std, int len, int type)
{
    char val_str[64];
    cJSON *root = cJSON_CreateObject();

    for (int i = 0; i < len; i++)
    {
        cJSON *item = cJSON_CreateObject();

        switch (std[i].key)
        {
        case 101:
        {
            cJSON_AddNumberToObject(root, "infrared", std[i].new_val.b_val+30);
            break;
        }
        case 102:
        {
            cJSON_AddStringToObject(root,"alarm",std[i].new_val.b_val?"true":"false");
            break;
        }
        case 103:
        {
            cJSON_AddStringToObject(root,"lamp",std[i].new_val.b_val?"true":"false");
            break;
        }
        case 104:
        {
            cJSON_AddNumberToObject(root,"tem",std[i].new_val.f_val);
            break;
        }
        case 105:
        {
            cJSON_AddNumberToObject(root,"hum",std[i].new_val.f_val);
            break;
        }
        case 106:
        {
            cJSON_AddStringToObject(root,"fan",std[i].new_val.b_val?"true":"false");
            break;
        }
        case 107:
        {
            cJSON_AddStringToObject(root,"irrigated",std[i].new_val.b_val?"true":"false");
            break;
        }
        case 108:
        {
            cJSON_AddStringToObject(root,"flame",std[i].new_val.b_val?"true":"false");
            break;
        }
        case 109:
        {
            cJSON_AddNumberToObject(root,"PM2.5",std[i].new_val.f_val);
            break;
        }
        case 301:
        {
            cJSON_AddStringToObject(root,"301",std[i].new_val.b_val?"true":"false");
            break;
        }
        case 302:
        {
            cJSON_AddStringToObject(root,"302",std[i].new_val.b_val?"true":"false");
            break;
        }
        default:
            break;
        }
    }
    printf("----发送完成----\n");
    char *json_str = cJSON_Print(root);
    cJSON_Delete(root); // 释放 cJSON 结构体本身
    return json_str;    // 返回新分配的字符串
}

int main(int argc, char const *argv[])
{

    /*解析点表*/
    FILE *fp;

    int std_num; // 设备数量
    int up_type; // 上报属性  //0-不上报，客户端主动采集；1-变化上报，即连续2次值不相等；2-周期上报
    int period;  // 上报周期

    fp = fopen("/home/hq/work/007/data_report/config.json", "r");
    if (fp == NULL)
    {
        perror("fopen err");
        return -1;
    }
    // 确保字符串以null终止，并处理实际读取的字节数 !!!
    size_t bytes_read = fread(config, 1, sizeof(config) - 1, fp); // 留一个字节给 null 终止符
    if (bytes_read == 0 && !feof(fp))
    { // 如果读取到0字节且不是文件末尾，说明有读取错误
        perror("Error reading config.json");
        fclose(fp); // 及时关闭文件
        return -1;
    }
    config[bytes_read] = '\0'; // 确保 config 字符串以 null 终止
    fclose(fp);                // 关闭文件

    cJSON *root = cJSON_Parse(config);
    if (!root)
    {
        fprintf(stderr, "Error parsing config JSON.\n");
        return -1;
    }

    cJSON *report = cJSON_GetObjectItem(root, "report");
    up_type = cJSON_GetObjectItem(report, "type")->valueint;
    period = cJSON_GetObjectItem(report, "period")->valueint;

    cJSON *stm32 = cJSON_GetObjectItem(root, "stm32");
    cJSON *data_32 = cJSON_GetObjectItem(stm32, "data");
    int num_32 = cJSON_GetArraySize(data_32);

    cJSON *modbus = cJSON_GetObjectItem(root, "modbus");
    cJSON *data_modbus = cJSON_GetObjectItem(modbus, "data");
    int num_modbus = cJSON_GetArraySize(data_modbus);
    std_num = num_32 + num_modbus;

    /*创建共享内存*/
    size_t shm_size = sizeof(int) + sizeof(struct std_node) * std_num;
    struct shm_param para;
    if (shm_init(&para, "shm", shm_size) < 0)
    {
        printf("shm init err\n");
        cJSON_Delete(root);
        return -1;
    }
    // 获取共享内存的起始地址N
    void *shm_base_addr = shm_getaddr(&para);
    if (shm_base_addr == (void *)-1 || shm_base_addr == NULL)
    {
        printf("shm get addr err\n");
        shm_del(&para);
        cJSON_Delete(root);
        return -1;
    }
    // 将共享内存的起始部分强制转换为我们的头部,并将设备数量写入头部
    int *header = (int *)shm_base_addr;
    // 数据数组紧随头部之后
    struct std_node *std_array = (struct std_node *)((char *)shm_base_addr + sizeof(int));

    MQTTClient client;                                                           // 创建客户端句柄
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer; // 链接选项
    MQTTClient_message pubmsg = MQTTClient_message_initializer;                  // 消息结构体
    MQTTClient_deliveryToken token;                                              // 消息交付令牌

    int rc; // 返回码

    MQTTClient_create(&client, ADDRESS, CLIENTID,
                      MQTTCLIENT_PERSISTENCE_NONE, NULL); // 创建客户端实例，并指定代理地址、客户端ID等
    // 设置连接选项
    conn_opts.keepAliveInterval = 20; // 心跳间隔，20秒
    conn_opts.cleansession = 1;       // 清理会话，表示不保留离线消息和订阅

    // 尝试与 MQTT 代理建立连接。
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        exit(EXIT_FAILURE);
    }
printf("----------------------\n");
    int timer_fd;
    struct itimerspec new_timer;
    int count = 0;

    // 创建一个 CLOCK_MONOTONIC 类型的定时器文件描述符
    // TFD_CLOEXEC: 确保在子进程中不会继承此FD

    timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);

    if (timer_fd == -1)
    {
        perror("timerfd_create failed");
        return 1;
    }
    printf("定时器文件描述符创建成功：fd = %d\n", timer_fd);

    // 设置定时器参数
    // 第一次到期时间
    new_timer.it_value.tv_sec = period;
    new_timer.it_value.tv_nsec = 0;
    // 周期性到期间隔
    new_timer.it_interval.tv_sec = period;
    new_timer.it_interval.tv_nsec = 0;

    // 启动定时器
    // 第一个参数是定时器FD
    // 第二个参数为0表示相对定时器 (相对于当前时间)
    // 第三个参数是新的定时器设置
    // 第四个参数为NULL表示不获取旧的定时器设置
    if (timerfd_settime(timer_fd, 0, &new_timer, NULL) == -1)
    {
        perror("timerfd_settime failed");
        close(timer_fd);
        return 1;
    }
    printf("定时器设置成功，将每%d秒触发一次。\n", period);

    // 使用 poll 监听定时器事件
    struct pollfd pfd;
    pfd.fd = timer_fd;   // 监听定时器文件描述符
    pfd.events = POLLIN; // 关注可读事件 (当定时器到期时可读)

    printf("进入主循环，等待定时器事件...\n");
    char *json_payload;
    while (1)
    {
        // poll 等待事件发生，-1 表示无限等待
        int ret = poll(&pfd, 1, -1);
        if (ret == -1)
        {
            perror("poll failed");
            break;
        }

        // 检查定时器FD是否可读
        if (pfd.revents & POLLIN)
        {
            uint64_t expirations;
            // 读取定时器到期次数，这会重置定时器内部的计数
            // 每次读取都会清除可读状态
            ssize_t s = read(timer_fd, &expirations, sizeof(expirations));
            if (s != sizeof(expirations))
            {
                perror("read timerfd failed");
                break;
            }

            // 处理定时器到期事件
            json_payload = serialize_info(std_array, std_num, 1);
            pubmsg.payload = json_payload;
            pubmsg.payloadlen = (int)strlen(json_payload);
            pubmsg.qos = QOS;
            pubmsg.retained = 0;
            MQTTClient_publishMessage(client, TOPIC, &pubmsg, &token); // 发布消息到指定主题
            free(json_payload);
        }
    }

    return 0;
}
